<html>
 <head>
  <meta charset="UTF-8">
 </head>
 <body>
  <h1 data-lake-id="wosFk" id="wosFk"><span data-lake-id="uea59d451" id="uea59d451">典型回答</span></h1>
  <p data-lake-id="ua3154ecc" id="ua3154ecc"><br></p>
  <p data-lake-id="ua8500f54" id="ua8500f54"><span data-lake-id="u3a246767" id="u3a246767">线程池是池化技术的一种典型实现，所谓池化技术就是提前保存大量的资源，以备不时之需。在机器资源有限的情况下，使用池化技术可以大大的提高资源的利用率，提升性能等。</span></p>
  <p data-lake-id="ucab2bdc2" id="ucab2bdc2"><span data-lake-id="u6e85b003" id="u6e85b003">​</span><br></p>
  <p data-lake-id="u95dc9300" id="u95dc9300"><span data-lake-id="ufddde596" id="ufddde596">线程池，说的就是提前创建好一批线程，然后保存在线程池中，当有任务需要执行的时候，从线程池中选一个线程来执行任务。</span></p>
  <p data-lake-id="u7e04ae38" id="u7e04ae38"><span data-lake-id="u4b405f95" id="u4b405f95">​</span><br></p>
  <p data-lake-id="u7222fa5a" id="u7222fa5a"><span data-lake-id="ue9dbac8d" id="ue9dbac8d">在编程领域，比较典型的池化技术有：</span></p>
  <p data-lake-id="ud3d87afd" id="ud3d87afd"><span data-lake-id="u12614c00" id="u12614c00">​</span><br></p>
  <p data-lake-id="u23b98675" id="u23b98675"><span data-lake-id="uc545b86e" id="uc545b86e">线程池、连接池、内存池、对象池等。</span></p>
  <p data-lake-id="u53ce2e1d" id="u53ce2e1d"><span data-lake-id="uf70c687f" id="uf70c687f">​</span><br></p>
  <p data-lake-id="u0ddc96dd" id="u0ddc96dd"><span data-lake-id="u4b6f5368" id="u4b6f5368">Java中线程池的继承关系如下：</span></p>
  <p data-lake-id="u6b6672c4" id="u6b6672c4"><span data-lake-id="u250ed5d0" id="u250ed5d0">​</span><br></p>
  <p data-lake-id="uf2db8036" id="uf2db8036"><img src="https://cdn.nlark.com/yuque/0/2023/png/5378072/1690526418418-4b39475b-cc1a-4a94-80f6-7ac9d9dfa326.png?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_20%2Ctext_SmF2YSA4IEd1IFA%3D%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10"></p>
  <p data-lake-id="uf40c6764" id="uf40c6764"><br></p>
  <h1 data-lake-id="NV9BY" id="NV9BY"><span data-lake-id="u51880be2" id="u51880be2">扩展知识</span></h1>
  <p data-lake-id="u4318ef50" id="u4318ef50"><br></p>
  <h2 data-lake-id="Ymh2P" id="Ymh2P"><span data-lake-id="u02f9415f" id="u02f9415f">Executors</span></h2>
  <p data-lake-id="u3b8c735d" id="u3b8c735d"><br></p>
  <p data-lake-id="ua1ebb599" id="ua1ebb599"><span data-lake-id="ub4047eb8" id="ub4047eb8">Executors的创建线程池的方法，创建出来的线程池都实现了ExecutorService接口。常用方法有以下几个：</span></p>
  <p data-lake-id="u237501f4" id="u237501f4"><span data-lake-id="u46198aaf" id="u46198aaf">​</span><br></p>
  <p data-lake-id="u027f6e79" id="u027f6e79"><span data-lake-id="u07d4d538" id="u07d4d538">newFixedThreadPool(int Threads)：创建固定数目线程的线程池。</span></p>
  <p data-lake-id="u01280d54" id="u01280d54"><span data-lake-id="u2d4ff4b5" id="u2d4ff4b5">​</span><br></p>
  <p data-lake-id="u5317534b" id="u5317534b"><span data-lake-id="u71db289a" id="u71db289a">newCachedThreadPool()：创建一个可缓存的线程池，调用execute 将重用以前构造的线程（如果线程可用）。如果没有可用的线程，则创建一个新线程并添加到池中。终止并从缓存中移除那些已有 60 秒钟未被使用的线程。</span></p>
  <p data-lake-id="ud1a872ee" id="ud1a872ee"><span data-lake-id="u22b12b62" id="u22b12b62">​</span><br></p>
  <p data-lake-id="uab19b2d3" id="uab19b2d3"><span data-lake-id="ua57a7f92" id="ua57a7f92">newSingleThreadExecutor()创建一个单线程化的Executor。</span></p>
  <p data-lake-id="u9cebde92" id="u9cebde92"><span data-lake-id="udae39baa" id="udae39baa">​</span><br></p>
  <p data-lake-id="ub34d4fde" id="ub34d4fde"><span data-lake-id="ube296214" id="ube296214">newScheduledThreadPool(int corePoolSize)创建一个支持定时及周期性的任务执行的线程池，多数情况下可用来替代Timer类。</span></p>
  <p data-lake-id="uff111ed1" id="uff111ed1"><br></p>
  <h2 data-lake-id="VtaqE" id="VtaqE"><span data-lake-id="u9351a62f" id="u9351a62f">线程池的实现原理</span></h2>
  <p data-lake-id="uc3a2c000" id="uc3a2c000"><br></p>
  <p data-lake-id="u5213295e" id="u5213295e"><span data-lake-id="ud6db5a61" id="ud6db5a61">通常，一般构造函数会反映出这个工具或这个对象的数据存储结构。</span></p>
  <p data-lake-id="u63eed2fd" id="u63eed2fd"><br></p>
  <p data-lake-id="u6e1abac0" id="u6e1abac0"><img src="https://cdn.nlark.com/yuque/0/2022/png/5378072/1665583372538-e0e9a869-89bd-4232-b627-b0de62c4e966.png?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_29%2Ctext_SmF2YSA4IEd1IFA%3D%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10"></p>
  <p data-lake-id="u120a4bca" id="u120a4bca"><br></p>
  <blockquote data-lake-id="udc897c79" id="udc897c79">
   <p data-lake-id="u485134b4" id="u485134b4"><span data-lake-id="u8035a095" id="u8035a095">如果把线程池比作一个公司。公司会有正式员工处理正常业务，如果工作量大的话，会雇佣外包人员来工作。</span></p>
   <p data-lake-id="u778e0268" id="u778e0268"><span data-lake-id="u382529d5" id="u382529d5"> </span></p>
   <p data-lake-id="u3b935926" id="u3b935926"><span data-lake-id="uff23572a" id="uff23572a">闲时就可以释放外包人员以减少公司管理开销。一个公司因为成本关系，雇佣的人员始终是有最大数。</span></p>
   <p data-lake-id="u4f30cff5" id="u4f30cff5"><span data-lake-id="u827758fa" id="u827758fa"> </span></p>
   <p data-lake-id="u33442f0c" id="u33442f0c"><span data-lake-id="u5750d1be" id="u5750d1be">如果这时候还有任务处理不过来，就走需求池排任务。</span></p>
  </blockquote>
  <p data-lake-id="u5853f35e" id="u5853f35e"><br></p>
  <ul list="u128db799">
   <li fid="u621ccc29" data-lake-id="uf7912d8a" id="uf7912d8a"><span data-lake-id="u88822f61" id="u88822f61"> acc : 获取调用上下文 </span></li>
   <li fid="u621ccc29" data-lake-id="u3fc3fa74" id="u3fc3fa74"><span data-lake-id="u032f1671" id="u032f1671"> corePoolSize: 核心线程数量，可以类比正式员工数量，常驻线程数量。 </span></li>
   <li fid="u621ccc29" data-lake-id="u0d1c4160" id="u0d1c4160"><span data-lake-id="ua4dfd34b" id="ua4dfd34b"> maximumPoolSize: 最大的线程数量，公司最多雇佣员工数量。常驻+临时线程数量。 </span></li>
   <li fid="u621ccc29" data-lake-id="uec7d4578" id="uec7d4578"><span data-lake-id="u1bc3785a" id="u1bc3785a"> workQueue：多余任务等待队列，再多的人都处理不过来了，需要等着，在这个地方等。 </span></li>
   <li fid="u621ccc29" data-lake-id="u8c3b9bde" id="u8c3b9bde"><span data-lake-id="u8491ec06" id="u8491ec06"> keepAliveTime：非核心线程空闲时间，就是外包人员等了多久，如果还没有活干，解雇了。 </span></li>
   <li fid="u621ccc29" data-lake-id="ubd05b9ea" id="ubd05b9ea"><span data-lake-id="ua54bc9c9" id="ua54bc9c9"> threadFactory: 创建线程的工厂，在这个地方可以统一处理创建的线程的属性。每个公司对员工的要求不一样，恩，在这里设置员工的属性。 </span></li>
   <li fid="u621ccc29" data-lake-id="ufd5fa55f" id="ufd5fa55f"><span data-lake-id="u354b2a0f" id="u354b2a0f"> handler：线程池拒绝策略，什么意思呢？就是当任务实在是太多，人也不够，需求池也排满了，还有任务咋办？默认是不处理，抛出异常告诉任务提交者，我这忙不过来了。 </span></li>
  </ul>
  <p data-lake-id="u7c4c6d42" id="u7c4c6d42"><br></p>
  <h3 data-lake-id="bf0ee04b" id="bf0ee04b"><span data-lake-id="u2750b35b" id="u2750b35b">添加一个任务</span></h3>
  <p data-lake-id="ue7714b2f" id="ue7714b2f"><br></p>
  <p data-lake-id="u67cd36ee" id="u67cd36ee"><span data-lake-id="u053abc24" id="u053abc24">接着，我们看一下线程池中比较重要的execute方法，该方法用于向线程池中添加一个任务。</span></p>
  <p data-lake-id="u68145d70" id="u68145d70"><br></p>
  <p data-lake-id="uccffd83b" id="uccffd83b"><img src="https://cdn.nlark.com/yuque/0/2022/png/5378072/1665583380723-ab7ae011-3928-4fea-9976-5064c495533f.png?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_24%2Ctext_SmF2YSA4IEd1IFA%3D%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10"></p>
  <p data-lake-id="uc9d3a6f5" id="uc9d3a6f5"><br></p>
  <p data-lake-id="u7feb7f20" id="u7feb7f20"><span data-lake-id="u38e1a4bc" id="u38e1a4bc">核心模块用红框标记了。 * 第一个红框：workerCountOf方法根据ctl的低29位，得到线程池的当前线程数，如果线程数小于corePoolSize，则执行addWorker方法创建新的线程执行任务；</span></p>
  <p data-lake-id="u0b37a03e" id="u0b37a03e"><br></p>
  <ul list="ufa34c4ab">
   <li fid="u73b766a0" data-lake-id="u3a97de01" id="u3a97de01"><span data-lake-id="u0d911645" id="u0d911645"> 第二个红框：判断线程池是否在运行，如果在，任务队列是否允许插入，插入成功再次验证线程池是否运行，如果不在运行，移除插入的任务，然后抛出拒绝策略。如果在运行，没有线程了，就启用一个线程。 </span></li>
   <li fid="u73b766a0" data-lake-id="u1e2245ff" id="u1e2245ff"><span data-lake-id="ucd951337" id="ucd951337"> 第三个红框：如果添加非核心线程失败，就直接拒绝了。 </span></li>
  </ul>
  <p data-lake-id="u615f442d" id="u615f442d"><br></p>
  <p data-lake-id="u85ca4804" id="u85ca4804"><span data-lake-id="u0db60cb7" id="u0db60cb7">这里逻辑稍微有点复杂，画了个流程图仅供参考</span></p>
  <p data-lake-id="u4756ea12" id="u4756ea12"><br></p>
  <p data-lake-id="u18868995" id="u18868995"><img src="https://cdn.nlark.com/yuque/0/2023/png/5378072/1694327729307-bcbe1c63-9cfe-4a5d-8b8b-3a5cc1bbdb93.png?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_19%2Ctext_SmF2YSA4IEd1IFA%3D%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10"></p>
  <p data-lake-id="u2ed650e8" id="u2ed650e8"><br></p>
  <p data-lake-id="u941cdcb4" id="u941cdcb4"><span data-lake-id="u19fa664a" id="u19fa664a">接下来，我们看看如何添加一个工作线程的？</span></p>
  <p data-lake-id="u0c31c3ba" id="u0c31c3ba"><br></p>
  <h3 data-lake-id="95a6954a" id="95a6954a"><span data-lake-id="uc5b3f52a" id="uc5b3f52a">添加worker线程</span></h3>
  <p data-lake-id="u592abff3" id="u592abff3"><br></p>
  <p data-lake-id="u8e4dd62b" id="u8e4dd62b"><span data-lake-id="u1f15f9c0" id="u1f15f9c0">从方法execute的实现可以看出：addWorker主要负责创建新的线程并执行任务，代码如下（这里代码有点长，没关系，也是分块的，总共有5个关键的代码块）：</span></p>
  <p data-lake-id="u4127f939" id="u4127f939"><br></p>
  <p data-lake-id="u3c4866e6" id="u3c4866e6"><img src="https://cdn.nlark.com/yuque/0/2022/png/5378072/1665583392891-88977751-8c66-43e5-97e3-4f67d7e5d5da.png?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_23%2Ctext_SmF2YSA4IEd1IFA%3D%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10"></p>
  <p data-lake-id="u57947dc3" id="u57947dc3"><br></p>
  <ul list="ud705a908">
   <li fid="uda8038c7" data-lake-id="u7826e6a2" id="u7826e6a2"><span data-lake-id="uffbbe8a5" id="uffbbe8a5">第一个红框：做是否能够添加工作线程条件过滤：</span></li>
  </ul>
  <p data-lake-id="u94b22d35" id="u94b22d35"><br></p>
  <p data-lake-id="u7a738e8e" id="u7a738e8e"><span data-lake-id="u53c54758" id="u53c54758">判断线程池的状态，如果线程池的状态值大于或等SHUTDOWN，则不处理提交的任务，直接返回；</span></p>
  <p data-lake-id="u3d93426e" id="u3d93426e"><br></p>
  <ul list="u1c868866">
   <li fid="u6ebf878f" data-lake-id="uf97d021d" id="uf97d021d"><span data-lake-id="u2298590c" id="u2298590c">第二个红框：做自旋，更新创建线程数量：</span></li>
  </ul>
  <p data-lake-id="u06df57e3" id="u06df57e3"><br></p>
  <p data-lake-id="uf23abfac" id="uf23abfac"><span data-lake-id="u39983760" id="u39983760">通过参数core判断当前需要创建的线程是否为核心线程，如果core为true，且当前线程数小于corePoolSize，则跳出循环，开始创建新的线程</span></p>
  <p data-lake-id="uf326cc15" id="uf326cc15"><br></p>
  <blockquote data-lake-id="u7e82a429" id="u7e82a429">
   <p data-lake-id="u3c90cf9e" id="u3c90cf9e"><span data-lake-id="u8028f24a" id="u8028f24a">有人或许会疑问 retry 是什么？这个是java中的goto语法。只能运用在break和continue后面。</span></p>
  </blockquote>
  <p data-lake-id="ua067d398" id="ua067d398"><br></p>
  <p data-lake-id="uceb37d40" id="uceb37d40"><span data-lake-id="u1eb1f2d9" id="u1eb1f2d9">接着看后面的代码：</span></p>
  <p data-lake-id="u6d3fc36e" id="u6d3fc36e"><br></p>
  <p data-lake-id="u22fa1137" id="u22fa1137"><img src="https://cdn.nlark.com/yuque/0/2022/png/5378072/1665583398448-96e1f25c-11d4-4f2d-963b-a77d5dc66605.png?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_31%2Ctext_SmF2YSA4IEd1IFA%3D%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10"></p>
  <p data-lake-id="u6fb296dc" id="u6fb296dc"><br></p>
  <ul list="uee7fbb88">
   <li fid="u7f62a817" data-lake-id="u2a65a007" id="u2a65a007"><span data-lake-id="ub82ac484" id="ub82ac484">第一个红框：获取线程池主锁。</span></li>
  </ul>
  <p data-lake-id="u21edefd5" id="u21edefd5"><br></p>
  <p data-lake-id="u6fc24008" id="u6fc24008"><span data-lake-id="udfa952f9" id="udfa952f9">线程池的工作线程通过Woker类实现，通过ReentrantLock锁保证线程安全。</span></p>
  <p data-lake-id="ub87ddd98" id="ub87ddd98"><br></p>
  <ul list="u45a2276d">
   <li fid="ufc69aedf" data-lake-id="ua4943a52" id="ua4943a52"><span data-lake-id="u0af50b2f" id="u0af50b2f"> 第二个红框：添加线程到workers中（线程池中）。 </span></li>
   <li fid="ufc69aedf" data-lake-id="uc5e33abe" id="uc5e33abe"><span data-lake-id="u38034fff" id="u38034fff"> 第三个红框：启动新建的线程。 </span></li>
  </ul>
  <p data-lake-id="ub5149e3f" id="ub5149e3f"><br></p>
  <p data-lake-id="u4c2e9f47" id="u4c2e9f47"><span data-lake-id="u5fb6ba2d" id="u5fb6ba2d">接下来，我们看看workers是什么。</span></p>
  <p data-lake-id="uff7d8577" id="uff7d8577"><br></p>
  <p data-lake-id="ud8fad580" id="ud8fad580"><img src="https://cdn.nlark.com/yuque/0/2022/png/5378072/1665583403290-03aa2525-8dea-45fd-bd66-f5080a87e520.png?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_22%2Ctext_SmF2YSA4IEd1IFA%3D%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10"></p>
  <p data-lake-id="u3f62d97e" id="u3f62d97e"><br></p>
  <p data-lake-id="ua3c63a08" id="ua3c63a08"><span data-lake-id="ubd9fc8f2" id="ubd9fc8f2">一个hashSet。所以，线程池底层的存储结构其实就是一个HashSet。</span></p>
  <p data-lake-id="u2daeb8ea" id="u2daeb8ea"><br></p>
  <h3 data-lake-id="d2188905" id="d2188905"><span data-lake-id="ube9c09e8" id="ube9c09e8">worker线程处理队列任务</span></h3>
  <p data-lake-id="ubde20998" id="ubde20998"><br></p>
  <p data-lake-id="ua970a570" id="ua970a570"><img src="https://cdn.nlark.com/yuque/0/2022/png/5378072/1665583408081-ea7bf94f-bfeb-47ed-ac54-35b29d74ce04.png?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_31%2Ctext_SmF2YSA4IEd1IFA%3D%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10"></p>
  <p data-lake-id="u3cf54e63" id="u3cf54e63"><br></p>
  <ul list="u36fb9ca6">
   <li fid="u12e75e95" data-lake-id="u0e886656" id="u0e886656"><span data-lake-id="u8ade7715" id="u8ade7715"> 第一个红框：是否是第一次执行任务，或者从队列中可以获取到任务。 </span></li>
   <li fid="u12e75e95" data-lake-id="u2f9917d3" id="u2f9917d3"><span data-lake-id="u22a7a999" id="u22a7a999"> 第二个红框：获取到任务后，执行任务开始前操作钩子。 </span></li>
   <li fid="u12e75e95" data-lake-id="ud5696284" id="ud5696284"><span data-lake-id="ue01a1a54" id="ue01a1a54"> 第三个红框：执行任务。 </span></li>
   <li fid="u12e75e95" data-lake-id="ud5ed32d0" id="ud5ed32d0"><span data-lake-id="uff0ea2b4" id="uff0ea2b4"> 第四个红框：执行任务后钩子。 </span></li>
  </ul>
  <p data-lake-id="u46acab39" id="u46acab39"><br></p>
  <p data-lake-id="uca8053e3" id="uca8053e3"><span data-lake-id="uf849c591" id="uf849c591">这两个钩子（beforeExecute，afterExecute）允许我们自己继承线程池，做任务执行前后处理。</span></p>
  <p data-lake-id="u9a0864e6" id="u9a0864e6"><br></p>
  <p data-lake-id="u3f82c4da" id="u3f82c4da"><span data-lake-id="u840f9227" id="u840f9227">到这里，源代码分析到此为止。接下来做一下简单的总结。</span></p>
  <p data-lake-id="u56b205f8" id="u56b205f8"><br></p>
  <h3 data-lake-id="25f9c7fa" id="25f9c7fa"><span data-lake-id="ue7240635" id="ue7240635">总结</span></h3>
  <p data-lake-id="u562d2ecd" id="u562d2ecd"><br></p>
  <p data-lake-id="u6f454914" id="u6f454914"><span data-lake-id="ue2ab94c7" id="ue2ab94c7">所谓线程池本质是一个hashSet。多余的任务会放在阻塞队列中。</span></p>
  <p data-lake-id="u1214bc7b" id="u1214bc7b"><br></p>
  <p data-lake-id="ufb7482c9" id="ufb7482c9"><span data-lake-id="u69e7dfdc" id="u69e7dfdc">只有当阻塞队列满了后，才会触发非核心线程的创建。所以非核心线程只是临时过来打杂的。直到空闲了，然后自己关闭了。</span></p>
  <p data-lake-id="ud491105d" id="ud491105d"><br></p>
  <p data-lake-id="ud7686363" id="ud7686363"><span data-lake-id="uaa952501" id="uaa952501">线程池提供了两个钩子（beforeExecute，afterExecute）给我们，我们继承线程池，在执行任务前后做一些事情。</span></p>
  <p data-lake-id="u01812b8d" id="u01812b8d"><br></p>
  <p data-lake-id="uada270ee" id="uada270ee"><span data-lake-id="u4e61b814" id="u4e61b814">线程池原理关键技术：锁（lock,cas）、阻塞队列、hashSet（资源池）</span></p>
  <p data-lake-id="u84259267" id="u84259267"><br></p>
  <p data-lake-id="u3d90b82f" id="u3d90b82f"><img src="https://cdn.nlark.com/yuque/0/2022/png/5378072/1665583415878-bc11357f-f6d2-4b8a-9cff-65208041b54e.png?x-oss-process=image%2Fwatermark%2Ctype_d3F5LW1pY3JvaGVp%2Csize_23%2Ctext_SmF2YSA4IEd1IFA%3D%2Ccolor_FFFFFF%2Cshadow_50%2Ct_80%2Cg_se%2Cx_10%2Cy_10"></p>
 </body>
</html>